﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Web.Mvc;
using System.Data;
using WisPortal.Data;

namespace WisPortal.Controls
{
	public class DataListControl
	{
		private StringBuilder sb = new StringBuilder();
        private XmlNode config = null;
        private ControllerContext viewContext;
        private ViewDataDictionary viewData;
        private AppUIParser parser;
        
        public bool ShowInForm { get; set; }
        public StringBuilder DialogStringBuilder { get; set; }
		
		private static int PAGER_MAXCOUNT = 11;

        public DataListControl(XmlNode node, ControllerContext vContext, ViewDataDictionary vData)
		{
            if (node == null || vContext == null || vData == null)
            {
                throw new CustomerException("Can not find data list configration.");
            }
            viewContext = vContext;
            viewData = vData;
            parser = new AppUIParser(viewContext,viewData);
			config = node;
		}


		public string GetHtml()
		{
			sb.Clear();
            
            //Prepare the data source
            PrepareDataSource(config);
            
			RenderNotifications(sb);
            
			sb.AppendLine("<div class=\"row-fluid\">\n\t<div class=\"span12\">");

			RenderTitle();
            //Tools
            parser.RenderTools(sb, config);
            parser.RenderExtraTools(sb, config);
			
			RenderQueryArea();
			RenderGrid();
				
			sb.AppendLine("</div>\n</div>");
			return sb.ToString();
		}

		protected void RenderNotifications(StringBuilder sb)
		{
			XmlNodeList notifications = config.SelectNodes(".//Notifications/Notification");
			foreach (XmlNode notification in notifications)
			{
				RenderNotification(sb,notification);
			}			
		}

		private void RenderNotification(StringBuilder sb,XmlNode notification)
		{
            string title = XmlHelper.GetAttributeValue(notification, "title");
            string description = notification.InnerText;
            string id = Guid.NewGuid().ToString("N");
            string type = XmlHelper.GetAttributeValue(notification,"type","");
            
            if (type.Equals("info"))
                type = "alert-info";
            else if (type.Equals("success"))
                type = "alert-success";
            else if (type.Equals("error"))
                type = "alert-error";
            else
                type = "";
            
			sb.AppendFormat("<div class=\"alert {1}\" id=\"notification_{0}\">", id,type);
            sb.AppendFormat("<a id=\"close_notification_{0}\" class=\"close\" data-dismiss=\"alert\" src=\"{1}\" alt=\"{2}\" title=\"{2}\">×</a>\n",
				id,
				UI.GetRelativeUrl(viewContext.HttpContext.Request.ApplicationPath,UI.GetThemePath("images/icons/icon_close.gif")),
				UI.Localize("Close"));
			sb.AppendFormat("<strong>{0}</strong>\n{1}\n", UI.Localize(title), UI.Localize(description));
			sb.AppendLine("</div>");
		}

        protected void RenderTitle()
        {
            if (XmlHelper.GetAttributeValue(config, "show_title", "").Equals("false"))
                return;
            string title = XmlHelper.GetAttributeValue(config, "title", "");
            string subTitle = XmlHelper.GetAttributeValue(config, "subtitle", "");
            //Titles
            if(title.Length > 0)
                sb.AppendFormat("<h3 class=\"heading\">{0}</h3>\n", title);
            if (subTitle.Length > 0)
                sb.AppendFormat("<div class=\"title\">-&nbsp;{0}</div>", subTitle);           
        }
		
		/// <summary>
		/// Query Area Generation
		/// </summary>
		protected void RenderQueryArea()
		{
            QueryControl qc = new QueryControl(this);
			XmlNode queryNode = config.SelectSingleNode("QueryArea");
            if (queryNode == null) return;
            XmlNodeList rows = config.SelectNodes("QueryArea/Rows/Row");
            string id = Guid.NewGuid().ToString("N");
			if (queryNode !=null && rows.Count == 0) return;

            sb.AppendLine("<div class=\"form-search well well-small\">");
			if(!ShowInForm)
			    sb.AppendFormat("<form action=\"{0}\" name=\"search_form\" method=\"get\" >",
                    XmlHelper.GetAttributeValue(queryNode,"form","."));
			sb.AppendLine("<table class=\"search-header\"><tbody>");
			foreach (XmlNode row in rows)
			{
				//Add a new row in query area 
				sb.AppendLine("<tr>");
				XmlNodeList columns = row.SelectNodes("Columns/Column");
				foreach (XmlNode column in columns)
				{
					string title = XmlHelper.GetAttributeValue(column, "title");
					string type = XmlHelper.GetAttributeValue(column, "type");
					//Process a column
					sb.AppendFormat("<td class=\"{0}\">",XmlHelper.GetAttributeValue(column,"type","search-field"));					
					//Add column title
					if (title.Length > 0)
						sb.AppendFormat("<label>{0}:</label>",title);					
					//Add column content                    
					sb.AppendLine("<div>");					
					//Insert all the query control instance
					XmlNodeList controls = column.SelectNodes("QueryControl");
					foreach (XmlNode control in controls)
					{
						sb.AppendLine(qc.GetControlHtml(control));
					}
					sb.AppendLine("     </div>");
					sb.AppendLine("</td>");
				}
				sb.AppendLine("</tr>");
			}
			sb.AppendLine("</tbody></table>");
            if (!ShowInForm)
			    sb.AppendLine("</form>");
			sb.AppendLine("</div>");
		}

		/// <summary>
		/// Grid View Generation
		/// </summary>
		protected void RenderGrid()
		{
			XmlNode gridNode = config.SelectSingleNode("GridView");
			if (gridNode == null) return;

			string pagerPosition = "";
			int pagerSize = 30;
			int totalCount = 0;
			int pos = 0;

			DataSet ds = GetDataSource(config, out totalCount);
            if (!ShowInForm)			
			    sb.AppendFormat("<form action=\"{0}\" method=\"post\" name=\"list_form\" class=\"cm-check-changes\">",XmlHelper.GetAttributeValue(gridNode,"form"));

			XmlNode pagerNode = gridNode.SelectSingleNode("Pager");
			
			if (pagerNode != null)
			{
				pagerPosition = XmlHelper.GetAttributeValue(pagerNode, "position",pagerPosition);
				pagerSize = int.Parse(XmlHelper.GetAttributeValue(pagerNode,"size",pagerSize.ToString()));
			}
			//Render top pager
			if (pagerPosition.Equals("both") || pagerPosition.Equals("top"))
				RenderPager(ds, pagerNode, pagerSize, totalCount, pos);

			//Render grid view
			RenderGridView(ds,gridNode);

			//Render bottom pager
			if (pagerPosition.Equals("both") || pagerPosition.Equals("bottom"))
				RenderPager(ds, pagerNode, pagerSize, totalCount, pos);
            if (!ShowInForm)
			    sb.AppendLine("</form>");
		}

        /// <summary>
        /// Load data source
        /// </summary>
        /// <param name="gridViewConfig"></param>
        protected void PrepareDataSource(XmlNode gridViewConfig)
        {
            DataMapping dm = new DataMapping(viewContext.HttpContext);
            XmlNodeList dataSources = gridViewConfig.SelectNodes("Data/DataSource");
            foreach (XmlNode dataSource in dataSources)
            {
                dm.SetDataset(dataSource, viewData);
            }
        }

        /// <summary>
        /// Get the dataset form configration
        /// </summary>
        /// <param name="gridViewConfig"></param>
        /// <param name="totalCount"></param>
        /// <returns></returns>
		protected DataSet GetDataSource(XmlNode gridViewConfig,out int totalCount)
		{
			totalCount = 0;
            XmlNode dataSourceConfig = gridViewConfig.SelectSingleNode("Data/DataSource");
            if(dataSourceConfig == null) return new DataSet();            
            //Check ViewData
            
            string dataSourceName = XmlHelper.GetAttributeValue(dataSourceConfig,"name");
            if (viewData[dataSourceName] != null)
            {
                return viewData[dataSourceName] as DataSet;
            }
            else
            {
                DataMapping dm = new DataMapping(viewContext.HttpContext);
                //SetDataset Method will set the dataset into viewData. So when GetDataSource function been called next time 
                //No more SetDataset function will be called.
                return dm.SetDataset(dataSourceConfig, viewData);                   
            }
		}

        public DataSet GetDataSource(string sourceName)
        {
            if (viewData[sourceName] != null)
            {
                return viewData[sourceName] as DataSet;
            }
            else
                return new DataSet();
        }



		/// <summary>
		/// Render the gridview body and tools
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="gridViewConfig"></param>
		protected void RenderGridView(DataSet ds,XmlNode gridViewConfig)
		{
            sb.AppendFormat("<table class=\"table table-striped\">");

			XmlNodeList columns = gridViewConfig.SelectNodes("Columns/Column");
			//Table Header
			sb.Append("<thead>\n<tr>");
			foreach (XmlNode column in columns)
			{
				RenderHeaderColumn(column);
			}
            sb.Append("</tr><thead>\n<tbody>");
			//Table Row
			if (ds != null && ds.Tables.Count > 0)
			{
				DataTable dt = ds.Tables[0];
				foreach (DataRow row in dt.Rows)
				{
					RenderRow(row, columns);
				}
			}

			sb.AppendFormat("</tbody></table>");

			//Render Tools
            parser.RenderExtraTools(sb, config);
		}

		/// <summary>
		/// Render Header column content
		/// </summary>
		/// <param name="config"></param>
		protected void RenderHeaderColumn(XmlNode config)
		{
			//Th attributes
			StringBuilder thAttr = ExtractNodeAttr(config);           
			sb.AppendFormat("<th {0}>",thAttr.ToString());

			//Render th content
			string thType = XmlHelper.GetAttributeValue(config, "type").ToLower().Trim();
			if (thType.Equals("checkbox"))
			{
				sb.AppendFormat("<input type=\"checkbox\" name=\"check_all\" value=\"Y\" title=\"Check / uncheck all\" class=\"checkbox cm-check-items\" />");
			}
			else if (thType.Equals("text") || thType.Equals("href"))
			{
				sb.AppendFormat("<span>{0}</span>",UI.Localize(XmlHelper.GetAttributeValue(config,"header_title")));
			}
			else
			{
				sb.AppendFormat("<span>{0}</span>", UI.Localize(XmlHelper.GetAttributeValue(config, "header_title")));
			}

			sb.AppendLine("</th>");
		}

		private static StringBuilder ExtractNodeAttr(XmlNode config)
		{
			StringBuilder thAttr = new StringBuilder();
			string className = XmlHelper.GetAttributeValue(config, "class");
			if (className.Length > 0)
				thAttr.AppendFormat("class=\"{0}\" ", className);
			string width = XmlHelper.GetAttributeValue(config, "width");
			if (width.Length > 0)
				thAttr.AppendFormat("width=\"{0}\" ", width);
			return thAttr;
		}

		/// <summary>
		/// Render table row
		/// </summary>
		/// <param name="row"></param>
		/// <param name="columns"></param>
		protected void RenderRow(DataRow row,XmlNodeList columns)
		{
			sb.AppendLine("<tr class=\"table-row\">");
			foreach (XmlNode column in columns)
			{
				RenderRowColumn(row, column);
			}
			sb.AppendLine("</tr>");
		}

		/// <summary>
		/// Render a table row column
		/// </summary>
		/// <param name="row"></param>
		/// <param name="configNode"></param>
		protected void RenderRowColumn(DataRow row,XmlNode configNode)
		{
			StringBuilder thAttr = ExtractNodeAttr(configNode);
			sb.AppendFormat("<td {0}>", thAttr.ToString());
			string type = XmlHelper.GetAttributeValue(configNode, "type").ToLower().Trim();
			string rowValue = GetColumnText(row, XmlHelper.GetAttributeValue(configNode, "field"));
            //Find the id field name
            string idFiledName = "ID";
            if (configNode.ParentNode != null && configNode.ParentNode.ParentNode != null)
                idFiledName = XmlHelper.GetAttributeValue(configNode.ParentNode.ParentNode, "id_field", idFiledName);

            string id = GetColumnText(row, idFiledName);
			if (type.Equals("text"))
			{
				sb.AppendFormat("{0}",rowValue);
			}
			else if (type.Equals("href"))
			{
				sb.AppendFormat("<a href='{1}'>{0}</a>",
                    rowValue,
                    parser.ParseCommand(row,configNode));
			}
			else if (type.Equals("checkbox"))
			{
				sb.AppendFormat("<input type=\"checkbox\" name=\"list_ids[]\" value=\"{0}\" class=\"checkbox cm-item\">",id);
			}
            else if (type.Equals("action"))
            {
                //Render Actions
                XmlNodeList actions = configNode.SelectNodes("Action");
                int i = 0;
                foreach (XmlNode action in actions)
                {
                    if (i > 0 && i < actions.Count)
                        //Insert "|"
                        sb.AppendLine("&nbsp;&nbsp;|");
                    RenderAction(row, action);
                    i++;
                }
            }
			else
			{
				sb.AppendLine(rowValue);
			}
			sb.AppendLine("</td>");
		}

		private string GetColumnText(DataRow row, string columnName)
		{
			if (columnName != null && columnName.Length > 0 && row[columnName] != null)
				return row[columnName].ToString();
			else
				return "NO COLUMN";
		}

        /// <summary>
        /// Render an action node
        /// </summary>
        /// <param name="row"></param>
        /// <param name="action"></param>
        protected void RenderAction(DataRow row,XmlNode action)
        {
            string type = XmlHelper.GetAttributeValue(action, "type");             
            parser.RenderAction(row,sb, action);
            parser.RenderDialog(action, this.DialogStringBuilder);
        }

		protected void RenderGridViewTools(XmlNode gridViewConfig)
		{
			////
			sb.AppendFormat("<div class=\"table-tools\"><a href=\"#users\" name=\"check_all\" class=\"cm-check-items cm-on underlined\">Select all</a>|<a href=\"#users\" name=\"check_all\" class=\"cm-check-items cm-off underlined\">Unselect all</a></div>");
		}

		/// <summary>
		/// Generate pager html
		/// </summary>
		/// <param name="ds"></param>
		/// <param name="gridViewConfig"></param>
		/// <param name="pagerSize"></param>
		protected void RenderPager(DataSet ds, XmlNode pagerNode, int pagerSize, int totalCount, int pos)
		{
            ////
            return;
			//string urlPattern = "";
			sb.AppendLine("<div id=\"pagination_contents\">");
			sb.AppendFormat("   <input type=\"hidden\" name=\"page\" value=\"{0}\">",pos);
			{
				//Begin pager
				sb.AppendLine("<div class=\"pagination clear cm-pagination-wraper top-pagination\">");
				{
					//Left go to page area
					sb.AppendLine("<div class=\"float-left\">");
					sb.AppendFormat("   <label>{0}:</label>", UI.Localize(XmlHelper.GetAttributeValue(pagerNode,"goto_page_text","Go to page")));
					sb.AppendFormat("   <input type=\"text\" class=\"input-text-short valign cm-pagination cm-history\" value=\"{0}\">",pos);
					sb.AppendFormat("   <img src=\"{1}\" class=\"pagination-go-button hand cm-pagination-button\" alt=\"{0}\" title=\"{0}\">", 
						UI.Localize(XmlHelper.GetAttributeValue(pagerNode, "goto_page_text", "Go")),
						UI.GetThemePath("images/icons/pg_right_arrow.gif"));
					sb.AppendLine("</div>");

					sb.AppendLine("<div class=\"float-right\">");
					//Previous
					sb.AppendFormat("<span class=\"lowercase\"><a name=\"pagination\" class=\" cm-history\">«&nbsp;{0}</a></span>",
						UI.Localize("pager_previous", XmlHelper.GetAttributeValue(pagerNode, "pager_previous", "Previous")));
				
					//Page numbers
					List<string> numbers = GetPageNumbers(totalCount, pagerSize, pos);
					foreach (string number in numbers)
					{
						if(number.Equals(pos.ToString()))
							sb.AppendFormat("<span class=\"strong\">{0}</span>",number);
						else if(number.Equals("..."))
							sb.AppendFormat("<span>{0}</span>",number);
						else
							sb.AppendFormat("<a name=\"pagination\" class=\"cm-ajax cm-history\" href=\"{0}\" rel=\"{1}\" rev=\"pagination_contents\">{1}</a>",
								UI.ReplaceParameterValue(viewContext.HttpContext.Request.RawUrl,"page",number));              
					}                
				
					//Next
					sb.AppendFormat("<span class=\"lowercase\"><a name=\"pagination\" class=\"cm-ajax cm-history\" href=\"{1}\" rel=\"{2}\" rev=\"pagination_contents\">{0}&nbsp;»</a></span>",
						UI.Localize("pager_next", XmlHelper.GetAttributeValue(pagerNode, "pager_next", "Next")),
						UI.ReplaceParameterValue(viewContext.HttpContext.Request.RawUrl,"page",(pos + 1).ToString()),
						pos+1);
					//Total
					sb.AppendFormat("<span class=\"pagination-total-items\">&nbsp;{0}:&nbsp;</span><span>{1}&nbsp;/</span>",
						UI.Localize("pager_total", XmlHelper.GetAttributeValue(pagerNode, "pager_total", "Total items")),
						totalCount);

					sb.AppendLine("</div>");
				}
				sb.AppendLine("</div>");
			}
			sb.AppendLine("</div>");
		}

		/// <summary>
		/// Calculate diplay numbers
		/// </summary>
		/// <remarks>
		/// There are less than 10 numbers will be return to 
		/// </remarks>
		/// <param name="total"></param>
		/// <param name="pageSize"></param>
		/// <param name="pos"></param>
		private List<string> GetPageNumbers(int total,int pageSize,int pos)
		{

			List<string> pageNumbers = new List<string>();
			int pageCount = Convert.ToInt32(Math.Ceiling((double)total / pageSize));

			if (pos < 0) pos = 1;
			if (pos > pageCount) pos = pageCount;

			if (pageCount <= PAGER_MAXCOUNT)
			{
				for (int i = 1; i <= pageCount; i++)
					pageNumbers.Add(i.ToString());
			}
			else
			{
				int start = Convert.ToInt32(Math.Max(1,pos - Math.Floor((double)PAGER_MAXCOUNT / 2)));
				int end = Convert.ToInt32(Math.Min(pageCount,pos + Math.Floor((double)PAGER_MAXCOUNT/2)));
				if (start > 1)
				{
					pageNumbers.Add("1");
					pageNumbers.Add("...");
				}
				for (int i = start; i <= end; i++)
				{
					pageNumbers.Add(i.ToString());
				}
				if (end < pageCount)
				{
					pageNumbers.Add("...");
					pageNumbers.Add(pageCount.ToString());
				}                
			}
			return pageNumbers;
		}
	}
}
